package chen.videoedit;

import android.media.MediaCodec;
import android.media.MediaCodecInfo;
import android.media.MediaExtractor;
import android.media.MediaFormat;
import android.os.Bundle;
import android.os.Environment;
import android.util.Log;
import android.view.View;

import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.util.ArrayList;

/**
 * mp3转AAC
 */
public class Mp3ToAacActivity extends BasisActivity {

    private String path_save;
    private String add_audio_path;
    private MediaExtractor mediaAudioExtractor;
    private int AudioTrack;
    private MediaFormat AudiomediaFormat;
    //解码
    private MediaCodec audio_decoder;
    private ByteBuffer[] decode_input;
    private ByteBuffer[] decode_output;
    private MediaCodec.BufferInfo decode_info;
    private ArrayList<byte[]> byteList;//PCM数据块容器

    private BufferedOutputStream bos;
    //编码
    private MediaCodec audio_encoder;
    private ByteBuffer[] encode_input;
    private ByteBuffer[] encode_output;
    private MediaCodec.BufferInfo encode_info;
    private boolean endTag = false;
    private byte[] mFrameByte;

    @Override
    public int setlayoutResID() {
        return R.layout.activity_mp3_to_aac;
    }

    @Override
    public void initialView(Bundle arg0) {
        findViewById(R.id.activity_mp3_to_aac_bt).setOnClickListener(this);

    }

    @Override
    public void setActivity() {
        path_save = Environment.getExternalStorageDirectory() + "/Audio_add_test.aac";//截取视频路径
        add_audio_path = Environment.getExternalStorageDirectory() + "/audio.mp3";//截取视频路径
        byteList = new ArrayList<>();//解析数据缓存
    }

    @Override
    public void onClick(View v) {

        switch (v.getId()) {
            case R.id.activity_mp3_to_aac_bt:
                MP3toAAC();
                break;
        }
    }


    private void MP3toAAC() {
        try {
            FileOutputStream fos = new FileOutputStream(new File(path_save));//初始化文件
            bos = new BufferedOutputStream(fos, 200 * 1024);
        } catch (FileNotFoundException e) {
            Log.e("error", "FileOutputStream 初始化失败" + e.toString());
        }
        PCMThread pt = new PCMThread();
        pt.start();
        AACThread at = new AACThread();
        at.start();

    }


    /**
     * 解码线程
     */
    class PCMThread extends Thread {
        @Override
        public void run() {
            MP3toPCM();
        }
    }

    /**
     * 编码线程
     */
    class AACThread extends Thread {
        @Override
        public void run() {
            PCMtoAAC();
        }
    }

    /**
     * 编码
     */
    private void PCMtoAAC() {
        initAudioEncoder();
        while (!endTag || !byteList.isEmpty()) {
            byte[] bytes = getPCMData();//获取解码器所在线程输出的数据 代码后边会贴上
            if (bytes == null) continue;
            int inputIndex = audio_encoder.dequeueInputBuffer(-1);//同解码器
            if (inputIndex < 0) continue;
            ByteBuffer inputBuffer = encode_input[inputIndex];//同解码器
            inputBuffer.clear();//同解码器
            int length = bytes.length;
            inputBuffer.limit(length);
            inputBuffer.put(bytes);//PCM数据填充给inputBuffer
            audio_encoder.queueInputBuffer(inputIndex, 0, length, 0, 0);//通知编码器 编码


            int out_index = audio_encoder.dequeueOutputBuffer(encode_info, 1000);//获取解码数据
            while (out_index >= 0) {
                ByteBuffer outputBuffer = encode_output[out_index];//获取编码数据
                int L = encode_info.size + 7;//给adts头字段空出7的字节
                if (mFrameByte == null || mFrameByte.length < L) {
                    mFrameByte = new byte[L];
                }
                addADTStoPacket(mFrameByte, L);
                outputBuffer.get(mFrameByte, 7, encode_info.size);
                Log.e("lll","写入");
                try {
                    bos.write(mFrameByte, 0, L);
                } catch (IOException e) {
                    e.printStackTrace();
                }
                audio_encoder.releaseOutputBuffer(out_index, false);//此操作一定要做，不然MediaCodec用完所有的Buffer后 将不能向外输出数据
                out_index = audio_encoder.dequeueOutputBuffer(encode_info, 1000);
            }
        }
        release();
        runOnUiThread(new Runnable() {
            @Override
            public void run() {
                Toast("完成");
            }
        });
    }

    /**
     * 解码
     */
    private void MP3toPCM() {
        mediaAudioExtractor = new MediaExtractor();//初始化分类器
        try {
            mediaAudioExtractor.setDataSource(add_audio_path);
        } catch (IOException e) {
            Log.e("error", "MediaExtractor音频初始化失败" + e.toString());
        }
        int Acount = mediaAudioExtractor.getTrackCount();
        for (int i = 0; i < Acount; i++) {
            MediaFormat format = mediaAudioExtractor.getTrackFormat(i);
            String mime = format.getString(MediaFormat.KEY_MIME);
            if (mime.startsWith("audio/")) {
                AudioTrack = i;//音轨
                AudiomediaFormat = format;//音轨格式
                Log.e("lll", format.toString());
            }
        }
        initAudioDecoder();

        mediaAudioExtractor.selectTrack(AudioTrack);//音频部分
        mediaAudioExtractor.seekTo(0, MediaExtractor.SEEK_TO_CLOSEST_SYNC);//从头开始解析
        while (true) {
            int inputBufferIndex = audio_decoder.dequeueInputBuffer(-1);//获取
            if (inputBufferIndex < 0) break;
            ByteBuffer buffer = decode_input[inputBufferIndex];
            buffer.clear();
            int sampleSize = mediaAudioExtractor.readSampleData(buffer, 0);//读取数据放入解码器
            if (sampleSize < 0) break;//sampleSize读取完成
            audio_decoder.queueInputBuffer(inputBufferIndex, 0, sampleSize, 0, 0);//通知解码器数据放入
            mediaAudioExtractor.advance();//读取下一步

            int outputBufferIndex = audio_decoder.dequeueOutputBuffer(decode_info, 1000);//获取解码数据
            while (outputBufferIndex >= 0) {
                ByteBuffer outputBuffer = decode_output[outputBufferIndex];//获取解析数据

                byte[] Bytes = new byte[decode_info.size];//BufferInfo内定义了此数据块的大小
                outputBuffer.get(Bytes);//将Buffer内的数据取出到字节数组中
                outputBuffer.clear();//数据取出后一定记得清空此Buffer MediaCodec是循环使用这些Buffer的，不清空下次会得到同样的数据
                putPCMData(Bytes);


                audio_decoder.releaseOutputBuffer(outputBufferIndex, false);//此操作一定要做，不然MediaCodec用完所有的Buffer后 将不能向外输出数据
                outputBufferIndex = audio_decoder.dequeueOutputBuffer(decode_info, 1000);
            }
        }
        endTag = true;

    }


    /**
     * 存入PCM数据
     */
    private void putPCMData(byte[] pcmChunk) {
        synchronized (AudioCodec.class) {//记得加锁
            byteList.add(pcmChunk);
        }
    }

    /**
     * 取出PCM数据
     */
    private byte[] getPCMData() {
        synchronized (AudioCodec.class) {//记得加锁
            if (byteList.isEmpty()) {
                return null;
            }
            byte[] pcmChunk = byteList.get(0);//每次取出index 0 的数据
            byteList.remove(pcmChunk);//取出后将此数据remove掉 既能保证PCM数据块的取出顺序 又能及时释放内存
            return pcmChunk;
        }
    }


    /**
     * 初始音频化解码器
     */
    private void initAudioDecoder() {
        try {
            audio_decoder = MediaCodec.createDecoderByType(MediaFormat.MIMETYPE_AUDIO_MPEG);
            audio_decoder.configure(AudiomediaFormat, null, null, 0);
        } catch (IOException e) {
            Log.e("error", "MediaCodec 音频解码器错误" + e.toString());
        }
        audio_decoder.start();//启动MediaCodec ，等待传入数据
        decode_input = audio_decoder.getInputBuffers();//MediaCodec在此ByteBuffer[]中获取输入数据
        decode_output = audio_decoder.getOutputBuffers();//MediaCodec将解码后的数据放到此ByteBuffer[]中 我们可以直接在这里面得到PCM数据
        decode_info = new MediaCodec.BufferInfo();//用于描述解码得到的byte[]数据的相关信息
    }

    // {mime=audio/mpeg, file-format=audio/mpeg, channel-count=2, bit-rate=128000, bitrate=128000, max-input-size=3000, durationUs=130374902, sample-rate=44100}


    /**
     * 初始音频化编码器
     */
    private void initAudioEncoder() {
        try {

            MediaFormat encodeFormat = MediaFormat.createAudioFormat(MediaFormat.MIMETYPE_AUDIO_AAC, 44100, 2);//参数对应-> mime type、采样率、声道数
            encodeFormat.setInteger(MediaFormat.KEY_BIT_RATE, 96000);//比特率
            encodeFormat.setInteger(MediaFormat.KEY_AAC_PROFILE, MediaCodecInfo.CodecProfileLevel.AACObjectLC);
            encodeFormat.setInteger(MediaFormat.KEY_MAX_INPUT_SIZE, 10 * 1024);

            audio_encoder = MediaCodec.createEncoderByType(MediaFormat.MIMETYPE_AUDIO_AAC);
            audio_encoder.configure(encodeFormat, null, null, MediaCodec.CONFIGURE_FLAG_ENCODE);
        } catch (IOException e) {
            Log.e("error", "MediaCodec 音频编码器错误" + e.toString());
        }
        audio_encoder.start();
        encode_input = audio_encoder.getInputBuffers();
        encode_output = audio_encoder.getOutputBuffers();
        encode_info = new MediaCodec.BufferInfo();
    }

    /**
     * 释放资源
     */
    private void release() {

        if (audio_decoder != null) {
            audio_decoder.stop();
            audio_decoder.release();
            audio_decoder = null;
        }
        if (audio_encoder != null) {
            audio_encoder.stop();
            audio_encoder.release();
            audio_encoder = null;
        }
        if (mediaAudioExtractor != null) {
            mediaAudioExtractor.release();
            mediaAudioExtractor = null;
        }
        if(bos!=null){
            try {
                bos.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

    /**
     * 添加ADTS头
     *
     * @param packet
     * @param packetLen
     */
    private void addADTStoPacket(byte[] packet, int packetLen) {
        int profile = 2; // AAC LC
        int freqIdx = 4; // 44.1KHz
        int chanCfg = 2; // CPE
        packet[0] = (byte) 0xFF;
        packet[1] = (byte) 0xF9;
        packet[2] = (byte) (((profile - 1) << 6) + (freqIdx << 2) + (chanCfg >> 2));
        packet[3] = (byte) (((chanCfg & 3) << 6) + (packetLen >> 11));
        packet[4] = (byte) ((packetLen & 0x7FF) >> 3);
        packet[5] = (byte) (((packetLen & 7) << 5) + 0x1F);
        packet[6] = (byte) 0xFC;
    }
}
